home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Palettes / MiscCircularSlider / MiscCircularSlider.subproj / MiscCSCell.m < prev    next >
Encoding:
Text File  |  1995-07-21  |  21.3 KB  |  750 lines

  1. //
  2. //    MiscCircularSliderCell.m -- a SliderCell for circular sliders
  3. //        Written by Vince DeMarco and Don Yacktman
  4. //        Copyright (c) 1994 by Vince DeMarco and Don Yacktman.
  5. //                Version 1.0  All rights reserved.
  6. //        This notice may not be removed from this source code.
  7. //
  8. //    This object is included in the MiscKit by permission from the author
  9. //    and its use is governed by the MiscKit license, found in the file
  10. //    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  11. //    for a list of all applicable permissions and restrictions.
  12. //    
  13.  
  14. // This was created by modifying Vince's CircularSliderCell class and
  15. // merging it with Don's RotationSliderCell class.  Here's the info
  16. // from the original files.
  17. /*
  18.  *    Filename:    CircularSliderCell.m 
  19.  *    Created :    Sun Oct 18 16:56:38 1992 
  20.  *    Author  :    Vince DeMarco
  21.  *        <vince@whatnxt.cuc.ab.ca>
  22.  *    LastEditDate was "Wed Feb  3 17:30:28 1993"
  23.  */
  24. //    RotationSliderCell.m -- written by Don Yacktman
  25. //    Copyright 1993 by Don Yacktman.  All rights reserved.
  26.  
  27. //    Modified by Laurent Daudelin on Wed Sep 7 12:09:55 1994
  28. //    to add support for lower and upper stop values, and
  29. //    to support for knob width adjustment for the MISC_SHUTTLEWHEEL_STYLE slider.
  30.  
  31. //  Modified by Carl Lindberg   Wed Apr 19 02:01:15 EDT 1995
  32. //  Added support for border types
  33. //  Added support for background color
  34. //  Fixed boundary-check bug, fixed bug when view is taller than it is wide
  35. //  Reorganized the code to something approaching sanity
  36. //  Redid drawing of knobImage, no more glitchiness on larger sliders/knob widths.
  37. //    (Unless you get really, really big :-) )
  38.  
  39. //  Modified more by Carl Lindberg Mon Jun 12 00:36:31 EDT 1995
  40. //  Added support for allowing which angle minValue is shown at
  41. //       (they don't have to "start" on the right side anymore)
  42. //  Added support for specifying which direction (clockwise or 
  43. //       clockwise) that values increase in.  Before, this was
  44. //       counterclockwise.
  45. //  Changed the pswrap to the pie-style one to accomodate above fixes
  46. //  The pie-style one previously gave strange values; doing the
  47. //       above modifications fixed this
  48.  
  49. #import "MiscCircularSliderCell.h"
  50. #import "MiscCSWraps.h"
  51. #import <math.h>
  52. #import <appkit/appkit.h>
  53.  
  54. @interface MiscCircularSliderCell(private)
  55. - _cacheImages;
  56. @end
  57.  
  58. // I changed how the knobImage gets drawn for slider-style a bit, so 
  59. // naturally I have a new set of fudges... :-)   -Carl
  60.  
  61. #define _KNOBSIZE_FUDGE            (- 0.1)
  62. #define _KNOBPOS_RADIUS_FUDGE    (-  .15)
  63. #define _KNOBPOS_X_FUDGE        (  1.3)
  64. #define _KNOBPOS_Y_FUDGE        (- 0.5)
  65.  
  66. // These are still used, though
  67. #define _KNOB_WIDTH_FUDGE (5.0 / 8.0)
  68. #define _KNOB_Y_FUDGE (- 1.0)
  69. #define _KNOB_X_FUDGE (  1.0)
  70. #define _BEZELS_FUDGE (  4.0)
  71. #define MISC_SLIDERCELL_CURRENT_VERSION 4
  72.  
  73. #define MISC_CSDEFAULTCOLOR NXConvertRGBAToColor(0.0, 0.0, 0.0, 0.0)  //transparent
  74.  
  75. @implementation MiscCircularSliderCell
  76.  
  77. // returns angle in 0-360 range
  78. inline static float in360(float ang)
  79. {    
  80.     while (ang < 0.0) ang += 360.0;
  81.     while (ang >= 360.0) ang -= 360.0;
  82.     return ang;
  83. }
  84.  
  85. // Given a "raw" angle (the angle if not clockwise and
  86. // startangle at 0), calculates the correct angle based
  87. // on the current values of isClockwise and startAngle.
  88. //  Used with currentangle() to get actual angle of
  89. // given value.
  90. float convertAngle(float ang, BOOL isClockwise, float startAngle)
  91. {
  92.     ang = in360(ang);
  93.     if (isClockwise) ang = startAngle-ang;
  94.     else ang = startAngle+ang;
  95.     return in360(ang);
  96. }
  97.  
  98. // Inverse of convertAngle().  Given the actual angle, uses
  99. // values of startAngle and isClockwise and startAngle to
  100. // convert to a "raw" angle (0-360, with start at 0), which then can be
  101. // mapped against minValue and maxValue to compute the value
  102. // of the current angle.  Used by position().
  103. float convertAngle2(float ang, BOOL isClockwise, float startAngle)
  104. {
  105.     ang = in360(ang);
  106.     ang -= startAngle;
  107.     if (isClockwise) ang = (360-ang);
  108.     return in360(ang);
  109. }
  110.  
  111. inline static float angle(float x, float y)
  112. { // this is a little cleaner than Vince's original routine.  :-)
  113.     double result = atan2(y, x) * (180 / M_PI);
  114.     return in360(result); // keep in 0-360 degree range
  115. }
  116.  
  117. inline static void xycoord(float angle, float radius,float *x, float *y)
  118. { // the inverse of the above; given angle and radius, determines x & y
  119.     *x = radius * (float)cos((M_PI / 180) * angle);
  120.     *y = radius * (float)sin((M_PI / 180) * angle);
  121. }
  122.  
  123. // Convert between a 0-360 degree angle and the slider's position.  The
  124. // angle is used as the parameter to pass to the pswrap that does the drawing.
  125. // These two #defines are inverses of each other.
  126. #define currentangle(pos, min, max) \
  127.     (convertAngle((360.0 * ((pos) - (min)) / ((max) - (min))),isClockwise,startAngle))
  128. #define position(angle, min, max) \
  129.     ((convertAngle2(angle, isClockwise,startAngle)) * ((max) - (min)) / 360.0 + (min))
  130.  
  131. static float distBetween(NXPoint a, NXPoint b)
  132. { return (float)hypot((double)(a.x-b.x),(double)(a.y-b.y)); }
  133.  
  134. + initialize 
  135. {
  136.     if (self == [MiscCircularSliderCell class]) {
  137.           [MiscCircularSliderCell setVersion:MISC_SLIDERCELL_CURRENT_VERSION];
  138.     }
  139.     return self;
  140. }
  141.  
  142. - init
  143. {
  144.     self = [super init];
  145.     minValue = 0.0;
  146.     value = 45.0;
  147.     maxValue = 360.0;
  148.     radius = 50.0; sliderPathWidth = 10.0;
  149.     lowerStopValue = minValue;
  150.     upperStopValue = maxValue;
  151.     center.x = radius;
  152.     center.y = radius;
  153.     style = MISC_SLIDER_STYLE;
  154.     jumpToMousePoint = YES;
  155.     hidden = NO;
  156.     backgroundColor = MISC_CSDEFAULTCOLOR;
  157.     borderType = MISC_CSNONE;
  158.     startAngle = 0;
  159.     isClockwise = NO;
  160.     [self _cacheImages];
  161.     return self;
  162. }
  163.  
  164. - awake
  165. {
  166.     id ret = [super awake];
  167.     [self _cacheImages];
  168.     return ret;
  169. }
  170.  
  171. #define _SETUPIMAGE PSsetgray(0.0); PSsetalpha(0.0); \
  172.         NXRectFill(&frame); PSsetalpha(1.0);
  173.  
  174. - _cacheImages
  175. {    // builds the cached slider, etc., if necessary.  (ie, size changed
  176.     // or no cache created yet... *****
  177.     NXSize knobSize, bezelSize = { radius * 2, radius * 2 };
  178.     NXRect frame = { { 0.0, 0.0 }, bezelSize };
  179.  
  180.     // don't cache for other styles -Carl
  181.       if (style != MISC_SLIDER_STYLE) return self;
  182.         
  183.     if (disabledBezelImage) [disabledBezelImage free];
  184.     disabledBezelImage = [[NXImage alloc] initSize:&bezelSize];
  185.     [disabledBezelImage lockFocus];
  186.     PSgsave(); _SETUPIMAGE;
  187.     MiscCSDrawBezel(0, 0, radius, sliderPathWidth, 0.666);
  188.     PSgrestore();
  189.     [disabledBezelImage unlockFocus];
  190.  
  191.     if (enabledBezelImage) [enabledBezelImage free];
  192.     enabledBezelImage = [[NXImage alloc] initSize:&bezelSize];
  193.     [enabledBezelImage lockFocus];
  194.     PSgsave(); _SETUPIMAGE;
  195.     MiscCSDrawBezel(0, 0, radius, sliderPathWidth, 0.5);
  196.     PSgrestore();
  197.     [enabledBezelImage unlockFocus];
  198.  
  199.     knobSize.width  = sliderPathWidth + _KNOBSIZE_FUDGE;
  200.     knobSize.height = sliderPathWidth + _KNOBSIZE_FUDGE;
  201.     if (knobImage) [knobImage free];
  202.     knobImage = [[NXImage alloc] initSize:&knobSize];
  203.     [knobImage lockFocus];
  204.     PSgsave(); _SETUPIMAGE;
  205.     MiscCSDrawKnob(0,0, sliderPathWidth + _KNOBSIZE_FUDGE);
  206.     PSgrestore();
  207.     [knobImage unlockFocus];
  208.     return self;
  209. }
  210.  
  211. - setSliderPathWidth:(float)val
  212. {
  213.     if (val == sliderPathWidth) return self;
  214.     sliderPathWidth = val;
  215.     [self _cacheImages];
  216.     return self;
  217. }
  218.  
  219. - (float)sliderPathWidth
  220. {
  221.     return sliderPathWidth;
  222. }
  223.  
  224. - setSliderStyle:(MiscCircularSliderStyle)aBOOL
  225. {
  226.     if (aBOOL == style) return self;
  227.     style = aBOOL;
  228.     [self _cacheImages];
  229.     return self;
  230. }
  231.  
  232. - (MiscCircularSliderStyle)sliderStyle
  233. {
  234.     return style;
  235. }
  236.  
  237. - setJumpToMousePoint:(BOOL)aBOOL
  238. {
  239.     jumpToMousePoint = aBOOL;
  240.     return self;
  241. }
  242.  
  243. - (BOOL)jumpToMousePoint
  244. {
  245.     return jumpToMousePoint;
  246. }
  247.  
  248. - setHidden:(BOOL)flag
  249. {
  250.     hidden = flag;
  251.     return self;
  252. }
  253.  
  254. - (BOOL)hidden
  255. {
  256.     return hidden;
  257. }
  258.  
  259. - setUpperStopValue:(float)aValue
  260. {
  261.     upperStopValue = aValue;
  262.     return self;
  263. }
  264.  
  265. - (float)upperStopValue
  266. {
  267.     return upperStopValue;
  268. }
  269.  
  270. - setLowerStopValue:(float)aValue
  271. {
  272.     lowerStopValue = aValue;
  273.     return self;
  274. }
  275.  
  276. - (float)lowerStopValue
  277. {
  278.     return lowerStopValue;
  279. }
  280.  
  281. - setUseBoundaries:(BOOL)flag
  282. {
  283.     useBoundaries = flag;
  284.     return self;
  285. }
  286.  
  287. - (BOOL)useBoundaries
  288. {
  289.     return useBoundaries;
  290. }
  291.  
  292. - read:(NXTypedStream *)stream
  293. {
  294. //  Looks like version 1 added upper/lower stop values, version 2 added an
  295. //  option to turn that feature off.
  296. //  Version 3 adds border type and a background color.
  297. //  Version 4 adds startAngle  -Carl
  298.     [super read:stream];
  299.     switch (NXTypedStreamClassVersion(stream, "MiscCircularSliderCell")) {
  300.          case MISC_SLIDERCELL_CURRENT_VERSION:
  301.             NXReadTypes(stream, "iccffffcifi",
  302.                 &style,
  303.                 &jumpToMousePoint,
  304.                 &hidden,
  305.                 &radius,
  306.                 &sliderPathWidth,
  307.                 &lowerStopValue,
  308.                 &upperStopValue,
  309.                 &useBoundaries,
  310.                 &borderType,
  311.                 &startAngle,
  312.                 &isClockwise);
  313.             NXReadPoint(stream, ¢er);
  314.             backgroundColor = NXReadColor(stream);
  315.             break;
  316.          case 3:
  317.             NXReadTypes(stream, "iccffffci",
  318.                 &style,
  319.                 &jumpToMousePoint,
  320.                 &hidden,
  321.                 &radius,
  322.                 &sliderPathWidth,
  323.                 &lowerStopValue,
  324.                 &upperStopValue,
  325.                 &useBoundaries,
  326.                 &borderType);
  327.             NXReadPoint(stream, ¢er);
  328.             backgroundColor = NXReadColor(stream);
  329.             startAngle = (style == MISC_PIECHART_STYLE) ? 90 : 0;
  330.             isClockwise = (style == MISC_PIECHART_STYLE) ? YES : NO;
  331.             break;
  332.         case 2:
  333.             NXReadTypes(stream, "iccffffc",
  334.                 &style,
  335.                 &jumpToMousePoint,
  336.                 &hidden,
  337.                 &radius,
  338.                 &sliderPathWidth,
  339.                 &lowerStopValue,
  340.                 &upperStopValue,
  341.                 &useBoundaries);
  342.             NXReadPoint(stream, ¢er);
  343.             borderType = MISC_CSNONE;
  344.             backgroundColor = MISC_CSDEFAULTCOLOR;
  345.             startAngle = (style == MISC_PIECHART_STYLE) ? 90 : 0;
  346.             isClockwise = (style == MISC_PIECHART_STYLE) ? YES : NO;
  347.             break;
  348.         case 1:
  349.             NXReadTypes(stream, "iccffff",
  350.                 &style,
  351.                 &jumpToMousePoint,
  352.                 &hidden,
  353.                 &radius,
  354.                 &sliderPathWidth,
  355.                 &lowerStopValue,
  356.                 &upperStopValue);
  357.             NXReadPoint(stream, ¢er);
  358.             useBoundaries = NO;
  359.             borderType = MISC_CSNONE;
  360.             backgroundColor = MISC_CSDEFAULTCOLOR;
  361.             startAngle = (style == MISC_PIECHART_STYLE) ? 90 : 0;
  362.             isClockwise = (style == MISC_PIECHART_STYLE) ? YES : NO;
  363.             break;
  364.         case 0:        /* read code for old version */
  365.             NXReadTypes(stream, "iccff",
  366.                 &style, &jumpToMousePoint, &hidden, &radius, &sliderPathWidth);
  367.             NXReadPoint(stream, ¢er);
  368.             upperStopValue = maxValue;
  369.             lowerStopValue = minValue;
  370.             useBoundaries = NO;
  371.             borderType = MISC_CSNONE;
  372.             backgroundColor = MISC_CSDEFAULTCOLOR;
  373.             startAngle = (style == MISC_PIECHART_STYLE) ? 90 : 0;
  374.             isClockwise = (style == MISC_PIECHART_STYLE) ? YES : NO;
  375.             break;
  376.      }
  377.     return self;
  378. }
  379.  
  380. - write:(NXTypedStream *)stream
  381. {
  382.     [super write:stream];
  383.     NXWriteTypes(stream, "iccffffcifi",
  384.         &style,
  385.         &jumpToMousePoint,
  386.         &hidden,
  387.         &radius,
  388.         &sliderPathWidth,
  389.         &lowerStopValue,
  390.         &upperStopValue,
  391.         &useBoundaries,
  392.         &borderType,                        /*NEW for version 3*/
  393.         &startAngle,                        /*NEW for version 4*/
  394.         &isClockwise);                      /*NEW for version 4*/
  395.     NXWritePoint(stream, ¢er);
  396.     NXWriteColor(stream, backgroundColor);  /*NEW for version 3*/
  397.     return self;
  398. }
  399.  
  400. // overridden methods here:
  401. - calcCellSize:(NXSize *)theSize inRect:(const NXRect *)aRect
  402. {
  403.     theSize->width = NX_WIDTH(aRect);
  404.     theSize->height = NX_HEIGHT(aRect);
  405.  
  406.     if (style != MISC_SLIDER_STYLE) return self;
  407.     // the "slider looking" style should always be a perfect circle.
  408.     if (theSize->height < theSize->width) theSize->width = theSize->height;
  409.     else theSize->height = theSize->width;
  410.     return self;
  411. }
  412.  
  413. #define _FINDXYFROMPOINT(z) \
  414.         if (imFlipped) \
  415.           y = NX_HEIGHT(&_lastViewFrame) - (z)->y - center.y; \
  416.         else y = (z)->y - center.y; \
  417.         x = (z)->x - center.x;
  418.  
  419. - (BOOL)startTrackingAt:(const NXPoint *)startPoint inView:controlView
  420. { // make sure we're within the slider's "hot" area, a circle (or ring)
  421.     float x, y, rad, ir;
  422.  
  423.     _FINDXYFROMPOINT(startPoint);
  424.     _offsetAng = angle(x, y) - currentangle(value, minValue, maxValue);
  425.     rad = x * x + y * y;
  426.     if (rad > radius * radius) return NO; // outside
  427.     ir = radius - sliderPathWidth - 3.0;
  428.     // If we are the slider style, don't respond to a click on the inner
  429.     // circle.  The second condition checks if the knob width is larger
  430.     // than the radius, meaning there is no inner circle and no reason
  431.     // to disallow the click.  -Carl
  432.     if ((style==MISC_SLIDER_STYLE) && (radius > sliderPathWidth) && (rad < ir*ir))
  433.         return NO;
  434.     return [super startTrackingAt:startPoint inView:controlView];  //return YES?
  435. }
  436.  
  437. - (BOOL)continueTracking:(const NXPoint *)lastPoint
  438.         at:(const NXPoint *)currentPoint inView:controlView
  439. {
  440.     float x, y, oldx, oldy, curAng, oldAng;
  441.  
  442.     if (hidden) return NO;
  443.  
  444.     // get the current angle of the mouse pointer
  445.     _FINDXYFROMPOINT(currentPoint);
  446.     curAng = angle(x, y);
  447.     if (!jumpToMousePoint) {
  448.         curAng -= _offsetAng;
  449.         if (curAng < 0) curAng += 360.0;
  450.     }
  451.     // get the last angle of the mouse pointer
  452.     oldAng = currentangle(value, minValue, maxValue);
  453.  
  454.     value = position(curAng, minValue, maxValue);
  455.     
  456.     // Do the boundary check; if we are outside those bounds, set
  457.     // the current value to be the bound the mouse is closest to. -Carl
  458.     if (useBoundaries && ((value > upperStopValue) || (value < lowerStopValue))) {
  459.         NXPoint upoint, lpoint, vpoint;
  460.         xycoord(currentangle(lowerStopValue,minValue,maxValue),radius,&lpoint.x,&lpoint.y);
  461.         xycoord(currentangle(upperStopValue,minValue,maxValue),radius,&upoint.x,&upoint.y);
  462.         xycoord(curAng,radius,&vpoint.x,&vpoint.y);
  463.         
  464.         if (distBetween(vpoint,lpoint) < distBetween(vpoint,upoint)) {
  465.             value = lowerStopValue;
  466.             curAng = currentangle(lowerStopValue, minValue, maxValue);
  467.         }
  468.         else {
  469.             value = upperStopValue;
  470.             curAng = currentangle(upperStopValue, minValue, maxValue);
  471.         }
  472.     }
  473.     
  474.     [[controlView window] disableFlushWindow];
  475.     PSgsave();
  476.     if (imFlipped) {
  477.         PSscale(1.0, -1.0);
  478.         PStranslate(0.0, -NX_HEIGHT(&_lastViewFrame));
  479.     }
  480.     if (style == MISC_SLIDER_STYLE) {
  481.         // Need to erase the old knob, which is done be re-compositing just that
  482.         // section.  The other styles do their own erasing in -drawKnob.
  483.         // Have to do this here, because it is only here we have the info
  484.         // on where the last knob position was, which the other styles don't need.
  485.         NXRect eraseRect; NXPoint imgPoint;
  486.         id bezel = (cFlags1.disabled ? disabledBezelImage : enabledBezelImage);
  487.         xycoord(oldAng, radius - sliderPathWidth / 2 - 2, &oldx, &oldy);
  488.         oldx += center.x - sliderPathWidth * _KNOB_WIDTH_FUDGE + _KNOB_X_FUDGE;
  489.         oldy += center.y - sliderPathWidth * _KNOB_WIDTH_FUDGE + _KNOB_Y_FUDGE;
  490.         NXSetRect(&eraseRect, oldx, oldy,
  491.                 sliderPathWidth * 1.5 + 2, sliderPathWidth * 1.5 + 2);
  492.         NXIntegralRect(&eraseRect);
  493.         NXInsetRect(&eraseRect, -4.0, -4.0);
  494.         imgPoint.x = NX_X(&eraseRect) + NX_X(&_lastFrame);
  495.         imgPoint.y = NX_Y(&eraseRect) + NX_Y(&_lastFrame);
  496.         [bezel composite:NX_SOVER fromRect:&eraseRect toPoint:&(imgPoint)];
  497.     }
  498.     PSgrestore();
  499.  
  500.     [self drawKnob:&_lastFrame];  // -drawKnob ignores its given rect; use as dummy.
  501.     [[[controlView window] reenableFlushWindow] flushWindow];
  502.     NXPing();
  503.     return YES;
  504. }
  505.  
  506.  
  507. // The basic drawing methods...
  508.  
  509. #define _COPYRECT(x, y) NXSetRect((x), \
  510.         NX_X(y), NX_Y(y), NX_WIDTH(y), NX_HEIGHT(y));
  511.  
  512. // This should draw the whole slider.  In here, we draw the
  513. // border, then the background color second (so to wipe out
  514. // any gray drawn by the border functions).  We then inset
  515. // the given rect a bit to account for the border, and call
  516. // -drawBarInside, which will draw the rest (via -drawKnob).
  517. // Hidden will just draw background color and return.
  518. // The reason I draw to a temporary NXImage here is so that
  519. // a background color with alpha in it will draw correctly.
  520. // When I drew directly to the view, the background would
  521. // start out as black, then NX_SOVER-like draw the background
  522. // color.  Fully transparent would end up black, and semi-
  523. // transparent would be much darker than you would expect.
  524. // This was the way I found to make it work.  A bit slower,
  525. // perhaps, but this method shouldn't get called all _that_
  526. // often. :-)  -Carl
  527.  
  528. - drawSelf:(const NXRect *)cellFrame inView:controlView
  529. {
  530.     NXRect ourRect;
  531.     NXPoint pt = cellFrame->origin;    
  532.     NXImage *img = [[NXImage alloc] initSize:&(cellFrame->size)];
  533.  
  534.     _COPYRECT(&ourRect,cellFrame);
  535.     _COPYRECT(&_lastViewFrame,cellFrame);
  536.     if ([controlView isFlipped]) imFlipped = YES;
  537.  
  538.     [img lockFocus];
  539.     
  540.     if (!hidden) switch (borderType) {
  541.         case MISC_CSGROOVE:
  542.             NXDrawGroove(&ourRect, &ourRect);
  543.             NXInsetRect(&ourRect,2.0,2.0);
  544.             break;
  545.         case MISC_CSBEZEL:
  546.             NXDrawGrayBezel(&ourRect, &ourRect);
  547.             NXInsetRect(&ourRect,2.0,2.0);
  548.             break;
  549.         case MISC_CSLINE:
  550.             PSsetgray(0.0);
  551.             NXFrameRect(&ourRect);
  552.             NXInsetRect(&ourRect,1.0,1.0);
  553.             break;
  554.         case MISC_CSBUTTON:
  555.             NXDrawButton(&ourRect, &ourRect);
  556.             NXInsetRect(&ourRect,2.0,2.0);
  557.             break;
  558.         case MISC_CSNONE:
  559.         default:break;
  560.     }
  561.     NXSetColor(backgroundColor); NXRectFill(&ourRect);
  562.     [img unlockFocus];
  563.     if (imFlipped) pt.y += NX_HEIGHT(cellFrame);
  564.     [img composite:NX_SOVER toPoint:&pt];
  565.     [img free];
  566.  
  567.     // I want to put this code inside of -drawBarInside, but
  568.     // when I do, it thinks enabledBezelImage is a Window class
  569.     // object!!  [I get a "Window does not respond to
  570.     // -composite:fromPoint:" runtime error in the switch
  571.     // statement in -drawBarInside.]  I am *clueless* as to
  572.     // why that's true.  It works if I put this code here, though.
  573.     if (!NXEqualRect(&_lastFrame, &ourRect)) {
  574.         _COPYRECT(&_lastFrame, &ourRect);
  575.         radius = MIN(NX_WIDTH(&ourRect), NX_HEIGHT(&ourRect)) / 2;
  576.         center.x = NX_X(&ourRect) + radius;
  577.         center.y = NX_Y(&ourRect) + radius;
  578.         [self _cacheImages];
  579.     }
  580.  
  581.     [self drawInside:&ourRect inView:controlView];
  582.     return self;
  583. }
  584.  
  585.  
  586. // This method should basically draw the slider, sans border and
  587. // background color, inside cellFrame.  It resets some instance
  588. // vars, draws whatever slider background is necessary, then calls
  589. // -drawKnob to do the rest.
  590. - drawBarInside:(const NXRect *)cellFrame flipped:(BOOL)flipped
  591. {
  592.     NXImage *bezel = (cFlags1.disabled ? disabledBezelImage : enabledBezelImage);
  593.     float r4 = radius - _BEZELS_FUDGE;
  594.  
  595.     // reset some instance vars....
  596.  
  597. /*  //WHY DOESN'T THIS WORK HERE!?!?!?!?
  598.     if (!NXEqualRect(&_lastFrame, cellFrame)) {
  599.         _COPYRECT(&_lastFrame, cellFrame);
  600.         radius = MIN(NX_WIDTH(cellFrame), NX_HEIGHT(cellFrame)) / 2;
  601.         center.x = NX_X(cellFrame) + radius;
  602.         center.y = NX_Y(cellFrame) + radius;
  603.         [self _cacheImages];
  604.     }
  605.  */
  606.     if (hidden) return self;
  607.     
  608.     PSgsave();
  609.     if (flipped) {
  610.         PSscale(1.0, -1.0);
  611.         PStranslate(0.0, -NX_HEIGHT(&_lastViewFrame));
  612.         imFlipped = YES;
  613.     }
  614.     switch(style) {
  615.         case MISC_SLIDER_STYLE:
  616.             [bezel composite:NX_SOVER toPoint:&(cellFrame->origin)];
  617.             break;
  618.         case MISC_PIECHART_STYLE:
  619.             if (cFlags1.disabled) break;    //otherwise do the background below
  620.         case MISC_DIAL_STYLE:
  621.         case MISC_SHUTTLEWHEEL_STYLE:
  622.             MiscCSDrawBackground(center.x, center.y, r4, radius, radius);
  623.             break;
  624.     }
  625.  
  626.     PSgrestore();
  627.     [self drawKnob:cellFrame];    //-drawKnob ignores given rect, use as dummy.
  628.     return self;
  629. }
  630.  
  631.  
  632. // This method will draw the filled piepiece/line/knob of the slider.
  633. // It actually ignores the knobRect parameter; it is only there because
  634. // this is the correct method to override.
  635. - drawKnob:(const NXRect *)knobRect
  636. {
  637.     NXPoint pos;
  638.     float ang, r4 = radius - _BEZELS_FUDGE;
  639.     NXSize knobSize;
  640.  
  641.     if (hidden) return self;
  642.  
  643.     PSgsave();
  644.     if (imFlipped) {
  645.         PSscale(1.0, -1.0);
  646.         PStranslate(0.0, -NX_HEIGHT(&_lastViewFrame));
  647.     }
  648.     ang = currentangle(value, minValue, maxValue);
  649.     switch (style) {
  650.         case MISC_SLIDER_STYLE:
  651.             // Composite the knobImage to the right spot. 
  652.             [knobImage getSize:&knobSize];
  653.             xycoord(ang, (radius-2) - (sliderPathWidth / 2) + _KNOBPOS_RADIUS_FUDGE, &pos.x, &pos.y);
  654.             pos.x += center.x - knobSize.width / 2.0 + _KNOBPOS_X_FUDGE;
  655.             pos.y += center.y - knobSize.height / 2.0 + _KNOBPOS_Y_FUDGE;
  656.             [knobImage composite:NX_SOVER toPoint:&pos];
  657.             break;
  658.         case MISC_PIECHART_STYLE: {
  659.             // With this style, I want the gray area to start with startAngle and go to
  660.             // the current angle.  Which one to start with depends on whether it's clockwise.
  661.             // So, just set the two params based on if it's clockwise.
  662.             float startang = (isClockwise)? ang:startAngle;
  663.             float finishang= (isClockwise)? startAngle:ang;
  664.  
  665.             if (cFlags1.disabled) {
  666.                 MiscCSPieChartDisabled(center.x, center.y, r4, radius, radius, startang, finishang);
  667.             } else {
  668.                 MiscCSPieChart(center.x, center.y, r4, radius, radius, startang, finishang);
  669.             }
  670.             break;
  671.         }
  672.         case MISC_DIAL_STYLE:
  673.             MiscCSControlDial(center.x, center.y, r4, radius, radius,
  674.                     ang, (cFlags1.disabled ? 0.333 : 0.0));
  675.             break;
  676.         case MISC_SHUTTLEWHEEL_STYLE:
  677.             if (cFlags1.disabled) {
  678.                 MiscCSControlKnobDisabled(center.x, center.y, r4, radius, radius);
  679.             } else {
  680.                 // Removed the following function call to allow adjustment of the
  681.                 // control know width by the following one. -Laurent Daudelin.
  682.                 //MiscCSControlKnob(center.x, center.y, r4, radius, radius, ang);
  683.                 MiscCSDrawShuttleKnob(center.x, center.y, radius, sliderPathWidth, ang);
  684.             }
  685.             break;
  686.     }
  687.     PSgrestore();
  688.     return self;
  689. }
  690.  
  691.  
  692. // I think this is what the superclass does anyways, but let's make sure
  693. - drawInside:(const NXRect *)cellFrame inView:controlView
  694. {
  695.     [self drawBarInside:cellFrame flipped:imFlipped];
  696.     return self;
  697. }
  698.  
  699. - getKnobRect:(NXRect *)knobRect flipped:(BOOL)flipped
  700. {
  701.     [super getKnobRect:knobRect flipped:flipped];
  702.     return self;
  703. }
  704.  
  705. - setBorderType:(int)type
  706. {
  707.     borderType = type;
  708.     return self;
  709. }
  710.  
  711. - (int)borderType
  712. {
  713.     return borderType;
  714. }
  715.  
  716. - setBackgroundColor:(NXColor)aColor
  717. {
  718.     backgroundColor = aColor;
  719.     return self;
  720. }
  721.  
  722. - (NXColor)backgroundColor
  723. {
  724.     return backgroundColor;
  725. }
  726.  
  727. - (float)startAngle
  728. {
  729.     return startAngle;
  730. }
  731.  
  732. - setStartAngle:(float)ang
  733. {
  734.     startAngle = in360(ang);
  735.     return self;
  736. }
  737.  
  738. - (BOOL)isClockwise
  739. {
  740.     return isClockwise;
  741. }
  742.  
  743. - setClockwise:(BOOL)clockwise
  744. {
  745.     isClockwise = clockwise;
  746.     return self;
  747. }
  748.  
  749. @end
  750.